#include <stdio.h>
#include <core/types.h>
#include <string.h>
#include <net/net.h>
#include <core/errno.h>
#include <core/init.h>
#include <malloc.h>
#include <sysconf.h>
#include <g-bios.h>
#include <arch/arm.h>


struct HostAddr
{
	struct sockaddr sockAddr;
	struct ListNode node;
};


static struct ListNode g_listHost;

static struct NetDevice *g_pCurNetDev; // fixme

static void GkEtherSendPacket(struct SocketBuffer *pSockBuff, const BYTE pbMacAddr[], UINT16 wEthType);


struct Socket *SearchSocket(const struct UDPHeader *, const struct IPHeader *);

static __INLINE__ void MacFillBcast(BYTE pbMacAddr[])
{
	memset(pbMacAddr,(BYTE)0xff, MAC_ADDR_LEN);
}


static __INIT__ int NetCoreInit(void)
{
	SUBSYS_INFO("Network Subsystem\n");

	InitSocket();

	InitListHead(&g_listHost);

	g_pCurNetDev = NULL;

	return 0;
}

SUBSYS_INIT(NetCoreInit);


static void ether_info(struct EtherHeader *pEthHead)
{

	printf("\tEther frame type: 0x%04x\n", BE16_TO_CPU(pEthHead->wFrameType));

	printf("\ttarget pbMacAddr: %02x:%02x:%02x:%02x:%02x:%02x\n", 
		pEthHead->pbDesMac[0],
		pEthHead->pbDesMac[1],
		pEthHead->pbDesMac[2],
		pEthHead->pbDesMac[3],
		pEthHead->pbDesMac[4],
		pEthHead->pbDesMac[5]
		);

	printf("\tsource pbMacAddr: %02x:%02x:%02x:%02x:%02x:%02x\n", 
		pEthHead->pbSrcMac[0],
		pEthHead->pbSrcMac[1],
		pEthHead->pbSrcMac[2],
		pEthHead->pbSrcMac[3],
		pEthHead->pbSrcMac[4],
		pEthHead->pbSrcMac[5]
		);
}


static void DumpSockBuff(const struct SocketBuffer *pSockBuff)
{
	int idx;
	BYTE *pbDataIter;

	for (idx = 0, pbDataIter = pSockBuff->pSkbHead; idx < pSockBuff->wSkbSize; idx++, pbDataIter++)
	{
		printf("%02x", *pbDataIter);
	}

	printf("\n");
}

//----------------- UDP Layer -----------------
void GUdpSendPacket(struct SocketBuffer *pSockBuff)
{
	struct UDPHeader *pUdpHdr;

	pSockBuff->pSkbData -= UDP_HDR_LEN;
	pSockBuff->wSkbSize += UDP_HDR_LEN;

	pUdpHdr = (struct UDPHeader *)pSockBuff->pSkbData;
	//
	pUdpHdr->wSrcPort = pSockBuff->pSock->addr.wSrcPort;
	pUdpHdr->wDesPort = pSockBuff->pSock->addr.wDesPort;
	pUdpHdr->wUdpLen  = CPU_TO_BE16(pSockBuff->wSkbSize);
	pUdpHdr->wChkSum  = 0;

	GIpSendPacket(pSockBuff, PROT_UDP);
}


struct SocketBuffer *GUdpRecvPacket(struct Socket *pSock)
{
	struct ListNode *pFirst;
	struct SocketBuffer *pSockBuff;
	int isEmtpy;
	ULONG psr;

	while (1)
	{
		LockIrqPsr(psr);
		if (!ListIsEmpty(&pSock->quRecv))
		{
			UnlockIrqPsr(psr);
			break;
		}
		UnlockIrqPsr(psr);
		udelay(5);
	}

	LockIrqPsr(psr);
	pFirst = pSock->quRecv.pNextNode;
	ListDelNode(pFirst);
	UnlockIrqPsr(psr);

	pSockBuff = MemToObj(pFirst, struct SocketBuffer, node);

	return pSockBuff;
}


static int UdpLayerDeliver(struct SocketBuffer *pSockBuff, const struct IPHeader *pIpHead)
{
	ULONG len;
	struct UDPHeader *pUdpHdr;
	struct Socket *pSock;

	pUdpHdr = (struct UDPHeader *)pSockBuff->pSkbData;

	pSockBuff->pSkbData += UDP_HDR_LEN;
	pSockBuff->wSkbSize -= UDP_HDR_LEN;

	pSock = SearchSocket(pUdpHdr, pIpHead);

	if (NULL == pSock)
	{
		BYTE *pbIP;

		pbIP = (BYTE *)pIpHead->pbSrcIP;
		// printf("Unexpected IP %d.%d.%d.%d/%d\n", pbIP[0], pbIP[1], pbIP[2], pbIP[3], pIpHead->pbDesIP[3]);
		GkSockBuffFree(pSockBuff);
		return -ENODEV;
	}

	pSock->addr.wDesPort = pUdpHdr->wSrcPort;

	ListAddTail(&pSockBuff->node, &pSock->quRecv);

	return 0;
}


static int InitPingPacket(struct PingPacket *pSndPingHdr, const char *pDataBuf, BYTE bType, UINT32 nSize, UINT16 nId, UINT16 nSeqNum)
{
	char *pFillData = NULL;
	UINT32 nHrdLen = sizeof(struct PingPacket);

	if (NULL == pSndPingHdr || NULL == pDataBuf)
	    return -EINVAL;
	    
	pSndPingHdr->type    = bType;
	pSndPingHdr->code    = 0;
	pSndPingHdr->wChkSum = 0;
	pSndPingHdr->wID     = nId;
	pSndPingHdr->seqno   = nSeqNum;

	pFillData = (char *)pSndPingHdr + nHrdLen;

	memcpy(pFillData, pDataBuf, nSize - nHrdLen);

	pSndPingHdr->wChkSum = ~ GuNetCheckSum(pSndPingHdr, nSize);

	return 0;
}


int PingSend(struct SocketBuffer *pRcvSockBuff, const struct IPHeader *pIpHead, BYTE bType)
{   
	BYTE   bPingBuf[PING_PACKET_LENGTH];
	UINT32 nHeaderLen = sizeof(struct PingPacket);
	struct Socket stSocket, *pSocket;
	struct SocketBuffer *pSendSockBuff;
	struct EtherHeader *pEthHead;
	struct PingPacket *pSndPingHdr, *pRcvPingHdr;

	pEthHead = (struct EtherHeader *)(pRcvSockBuff->pSkbData - IP_HDR_LEN - ETH_HDR_LEN);

	pRcvPingHdr = (struct PingPacket *)pRcvSockBuff->pSkbData;
	
	pSndPingHdr = (struct PingPacket *)bPingBuf;
	
	pSendSockBuff = GkSockBuffAlloc(ETH_HDR_LEN + IP_HDR_LEN, PING_PACKET_LENGTH);
	if (NULL == pSendSockBuff)
	{
	    printf("%s(): GkSockBuffAlloc return null\n", __FUNCTION__);
	    return -EBUSY;
	}

	memcpy(stSocket.addr.pbDesIP, pIpHead->pbSrcIP, IPV4_ADDR_LEN);

	memcpy(stSocket.addr.pbDesMac, pEthHead->pbSrcMac, MAC_ADDR_LEN);

	pSendSockBuff->pSock = &stSocket;

	InitPingPacket(pSndPingHdr, (char *)pRcvPingHdr + nHeaderLen, bType, PING_PACKET_LENGTH, pRcvPingHdr->wID, pRcvPingHdr->seqno);
	
	memcpy(pSendSockBuff->pSkbData, pSndPingHdr, PING_PACKET_LENGTH);

	GIpSendPacket(pSendSockBuff, PROT_ICMP);

	return 0;    
}


int PingRevice(struct SocketBuffer *pRcvSockBuff, const struct IPHeader *pIpHead)
{
	struct PingPacket *pPingHdr;

	pPingHdr = (struct PingPacket *)pRcvSockBuff->pSkbData;

	printf("%d bytes from %d.%d.%d.%d: icmp_seq=%d ttl=%d time=12.7 ms\n", // fixme :-)
	        pRcvSockBuff->wSkbSize,
	        pIpHead->pbSrcIP[0],
	        pIpHead->pbSrcIP[1],
	        pIpHead->pbSrcIP[2],
	        pIpHead->pbSrcIP[3],
	        BE16_TO_CPU(pPingHdr->seqno),
	        pIpHead->bTimeToLive);
	        
	return 0;
}


int PingReqest(struct Socket *socket)
{
	BYTE   bPingBuf[PING_PACKET_LENGTH];
	UINT32 nHeaderLen = sizeof(struct PingPacket);
	UINT32 i;
	volatile UINT32 j;
	struct SocketBuffer *pSendSockBuff;
	struct PingPacket *pSndPingHdr;

	
	for (i = 0; i < PING_MAX_TIMES; i++)
	{
	    pSendSockBuff = GkSockBuffAlloc(ETH_HDR_LEN + IP_HDR_LEN, PING_PACKET_LENGTH);
	    if (NULL == pSendSockBuff)
	    {
	        printf("%s(): GkSockBuffAlloc return null\n", __FUNCTION__);
	        return -EBUSY;
	    }
	    
	    pSendSockBuff->pSock = socket;

	    pSndPingHdr = (struct PingPacket *)bPingBuf;

	    InitPingPacket(pSndPingHdr, bPingBuf + nHeaderLen, ICMP_TYPE_ECHO_REQUEST, PING_PACKET_LENGTH, CPU_TO_BE16(PING_DEFUALT_PID), CPU_TO_BE16(i + 1));    

	    memcpy(pSendSockBuff->pSkbData, pSndPingHdr, PING_PACKET_LENGTH);

	  	GIpSendPacket(pSendSockBuff, PROT_ICMP);

		udelay(1000000UL);
	}

	return 0;
}


static int IcmpDeliver(struct SocketBuffer *pSockBuff, const struct IPHeader *pIpHead)
{
	struct PingPacket *pPingHdr;
	struct EtherHeader *pEthHead;

	pPingHdr = (struct PingPacket *)pSockBuff->pSkbData;

	pEthHead = (struct EtherHeader *)(pSockBuff->pSkbData - IP_HDR_LEN - ETH_HDR_LEN);

//	printf("icmp seq:%d\n", BE16_TO_CPU(pPingHdr->seqno));

	switch(pPingHdr->type)
	{
	case ICMP_TYPE_ECHO_REQUEST:
	    PingSend(pSockBuff, pIpHead, ICMP_TYPE_ECHO_REPLY);
	    
	    break;
	    
	case ICMP_TYPE_ECHO_REPLY:
	    PingRevice(pSockBuff, pIpHead);
	    
	    break;
	    
	case ICMP_TYPE_DEST_UNREACHABLE:
	    printf("From %d.%d.%d.%d: icmp_seq=%d Destination Host Unreachable\n", 
	            pIpHead->pbDesIP[0],
	            pIpHead->pbDesIP[1],
	            pIpHead->pbDesIP[2],
	            pIpHead->pbDesIP[3],
	            BE16_TO_CPU(pPingHdr->seqno));
	        
	    break;
	    
	default:
	    break;
	}
	
	return 0;
}

	
int IpLayerDeliver(struct SocketBuffer *pSockBuff)
{
	struct IPHeader *pIpHead;

	pIpHead = (struct IPHeader *)pSockBuff->pSkbData;

	pSockBuff->pSkbData += IP_HDR_LEN;
	pSockBuff->wSkbSize = BE16_TO_CPU(pIpHead->wTotalLen) - IP_HDR_LEN;

	if (IP_HDR_LEN != ((pIpHead->bVerHdrLen & 0xf) << 2))
	{
		printf("Warning: pIpHead head len not match\n");
		return -1;
	}


	switch(pIpHead->bUpProt)
	{
	case PROT_UDP:
		// printf("\tUDP received!\n");
		UdpLayerDeliver(pSockBuff, pIpHead);
		break;

	case PROT_ICMP:
		// printf("\n\tICMP received!\n");
		IcmpDeliver(pSockBuff, pIpHead);
		break;

	case PROT_TCP:
		// printf("\tTCP received!\n");
		GkSockBuffFree(pSockBuff);
		break;

	case PROT_IGMP:
		printf("\tIGMP received!\n");
		GkSockBuffFree(pSockBuff);
		break;

	case PROT_OSPF:
		printf("\tOSPF received!\n");
		GkSockBuffFree(pSockBuff);
		break;

	default:
		printf("\n\tUnknown IP Protocol!\n");
		GkSockBuffFree(pSockBuff);
		return 0;
	}


	return pIpHead->bUpProt;
}


void GIpSendPacket(struct SocketBuffer *pSockBuff, BYTE bProtocal)
{
	struct IPHeader *pIpHead;
	UINT32 dwSrcIP;


	pSockBuff->pSkbData -= IP_HDR_LEN;
	pSockBuff->wSkbSize += IP_HDR_LEN;

	pIpHead = (struct IPHeader *)pSockBuff->pSkbData;

	pIpHead->bVerHdrLen  = 0x45;
	pIpHead->bTOS        = 0;
	pIpHead->wTotalLen   = (UINT16)CPU_TO_BE16((UINT16)pSockBuff->wSkbSize);
	pIpHead->wID         = (UINT16)CPU_TO_BE16(0); //
	pIpHead->wFlagFrag   = (UINT16)CPU_TO_BE16(0x4000);
	pIpHead->bTimeToLive = 0xff;
	pIpHead->bUpProt     = bProtocal;
	pIpHead->wChkSum     = 0;

	GuNetGetIP(NULL, &dwSrcIP);
	memcpy(pIpHead->pbSrcIP, &dwSrcIP, IPV4_ADDR_LEN);

	memcpy(&pIpHead->pbDesIP, pSockBuff->pSock->addr.pbDesIP, IPV4_ADDR_LEN);

	pIpHead->wChkSum = ~ GuNetCheckSum(pIpHead, IP_HDR_LEN);

	GkEtherSendPacket(pSockBuff, pSockBuff->pSock->addr.pbDesMac, ETH_TYPE_IP);
}


//------------------------ ARP Layer -------------------------
void ArpSendPacket(const BYTE nip[], const BYTE *pbMacAddr, UINT16 wArpOper)
{
	struct SocketBuffer *pSockBuff;
	struct ArpPacket *pArpPkt;
	BYTE vbMacAddr[MAC_ADDR_LEN];
	UINT32 dwSrcIP;


	pSockBuff = GkSockBuffAlloc(ETH_HDR_LEN, ARP_PKT_LEN);
	if (NULL == pSockBuff)
	{
		printf("%s: fail to alloc pSockBuff!\n", __FUNCTION__);
		return;
	}

	pArpPkt = (struct ArpPacket *)pSockBuff->pSkbData;

	pArpPkt->wHardType = CPU_TO_BE16(1);
	pArpPkt->wProtType = ETH_TYPE_IP;
	pArpPkt->bHardSize = MAC_ADDR_LEN;
	pArpPkt->bProtSize = IPV4_ADDR_LEN;
	pArpPkt->wArpOpCode = wArpOper;

	GuNetGetMac(NULL, vbMacAddr); //fixme for failure
	memcpy(pArpPkt->pbSrcMac, vbMacAddr, MAC_ADDR_LEN);

	GuNetGetIP(NULL, &dwSrcIP);
	memcpy(pArpPkt->pbSrcIP, &dwSrcIP, IPV4_ADDR_LEN);

	if (NULL == pbMacAddr)
		MacFillBcast(pArpPkt->pbDesMac);
	else
		memcpy(pArpPkt->pbDesMac, pbMacAddr, MAC_ADDR_LEN);
	memcpy(pArpPkt->pbDesIP, nip, IPV4_ADDR_LEN);

	GkEtherSendPacket(pSockBuff, pArpPkt->pbDesMac, ETH_TYPE_ARP);
}


static const char *g_arp_pDesc[] = {"N/A", "Request", "Reply"};
//
static int ArpHandlePacket(struct SocketBuffer *pSockBuff)
{
	struct ArpPacket *pArpPkt;
	ULONG sz;
	UINT32 ip;

	pArpPkt = (struct ArpPacket *)pSockBuff->pSkbData;

	if (pArpPkt->wProtType != ETH_TYPE_IP)
	{
		printf("\tProt Error!\n");
		return -1;
	}

	memcpy(&ip, pArpPkt->pbSrcIP, 4);

#if 0
	printf("\t%s ARP received from: %d.%d.%d.%d\n",
			g_arp_pDesc[BE16_TO_CPU(pArpPkt->wArpOpCode)],
			pArpPkt->pbSrcIP[0],
			pArpPkt->pbSrcIP[1],
			pArpPkt->pbSrcIP[2],
			pArpPkt->pbSrcIP[3]
			);
#endif

	switch(pArpPkt->wArpOpCode)
	{
	case ARP_OP_REP:
		if (getaddr(ip) == NULL)
		{
			struct HostAddr *pHostAddr;

			pHostAddr = malloc(sizeof(struct HostAddr));
			if (NULL == pHostAddr)
				return -ENOMEM;
			
			memcpy(pHostAddr->sockAddr.pbDesIP, pArpPkt->pbSrcIP, 4);
			memcpy(pHostAddr->sockAddr.pbDesMac, pArpPkt->pbSrcMac, 6);
#if 0
			printf("ARP %d.%d.%d.%d<-->%02x.%02x.%02x.%02x.%02x.%02x\n",
				   pHostAddr->sockAddr.pbDesIP[0],
				   pHostAddr->sockAddr.pbDesIP[1],
				   pHostAddr->sockAddr.pbDesIP[2],
				   pHostAddr->sockAddr.pbDesIP[3],
				   pHostAddr->sockAddr.pbDesMac[0],
				   pHostAddr->sockAddr.pbDesMac[1],
				   pHostAddr->sockAddr.pbDesMac[2],
				   pHostAddr->sockAddr.pbDesMac[3],
				   pHostAddr->sockAddr.pbDesMac[4],
				   pHostAddr->sockAddr.pbDesMac[5]
				   );
#endif

			ListAddTail(&pHostAddr->node, &g_listHost);
		}

		break;

	case ARP_OP_REQ:
		ArpSendPacket(pArpPkt->pbSrcIP, pArpPkt->pbSrcMac, ARP_OP_REP);
		break;

	default:
		printf("\t%s(): wArpOpCode error!\n", __FUNCTION__);
		break;
	}

	GkSockBuffFree(pSockBuff);

	return 0;
}


//-----------------------------------------------
int GkNetRxDcp(struct SocketBuffer *pSockBuff)
{
	struct EtherHeader *pEthHead;
	ULONG len;


	pEthHead = (struct EtherHeader *)pSockBuff->pSkbData;

	pSockBuff->pSkbData += ETH_HDR_LEN;
	pSockBuff->wSkbSize -= ETH_HDR_LEN;

	switch(pEthHead->wFrameType)
	{
	case ETH_TYPE_ARP:
		// printf("\tARP received.\n");
		ArpHandlePacket(pSockBuff);
		break;

	case ETH_TYPE_RARP:
		// printf("\tRARP packet received.\n");
		break;

	case ETH_TYPE_IP:
		// printf("\tIP packet received.\n");
		IpLayerDeliver(pSockBuff);
		break;

	default:
		// printf("\tframe type error (= 0x%04x)!\n", pEthHead->wFrameType);
		break;
	}

	return 0;
}

//------------------ Send Package to Hardware -----------------
void GkEtherSendPacket(struct SocketBuffer *pSockBuff, const BYTE pbMacAddr[], UINT16 wEthType)
{
	struct EtherHeader *pEthHead;
	BYTE vbMacAddr[MAC_ADDR_LEN];


	pSockBuff->pSkbData -= ETH_HDR_LEN;
	pSockBuff->wSkbSize += ETH_HDR_LEN;

	if (pSockBuff->pSkbData != pSockBuff->pSkbHead)
	{
		printf("pSockBuff len error!\n");
		return;
	}

	pEthHead = (struct EtherHeader *)pSockBuff->pSkbData;

	memcpy(pEthHead->pbDesMac, pbMacAddr, MAC_ADDR_LEN);

	GuNetGetMac(NULL, vbMacAddr); //fixme for failure
	memcpy(pEthHead->pbSrcMac, vbMacAddr, MAC_ADDR_LEN);

	pEthHead->wFrameType = wEthType;

	g_pCurNetDev->SendPacket(g_pCurNetDev, pSockBuff);

	GkSockBuffFree(pSockBuff);
}


UINT16 GuNetCheckSum(void *pBuff, ULONG nLen)
{
	UINT32	lChkSum;
	UINT16 *p;
	ULONG n;

	lChkSum = 0;

	for (n = nLen, p = pBuff; n > 0; n -= 2, p++)
		lChkSum += *p;

	lChkSum = (lChkSum & 0xffff) + (lChkSum >> 16);
	lChkSum = (lChkSum & 0xffff) + (lChkSum >> 16);

	return (lChkSum & 0xffff);
}


struct SocketBuffer *GkSockBuffAlloc(ULONG nProtLen, ULONG nDataLen)
{
	struct SocketBuffer *pSockBuff;

	pSockBuff = malloc(sizeof(struct SocketBuffer));
	if (NULL == pSockBuff)
		return NULL;

	pSockBuff->pSkbHead = malloc(nProtLen + nDataLen);
	if (NULL == pSockBuff->pSkbHead)
	{
		printf("%s(): malloc failed (size = %d bytes)!\n", __FUNCTION__, nProtLen + nDataLen);
		return NULL;
	}

	pSockBuff->pSkbData = pSockBuff->pSkbHead + nProtLen;
	//pSockBuff-->pSkbTail = pSockBuff->pSkbData + nDataLen;
	pSockBuff->wSkbSize  = nDataLen;

	InitListHead(&pSockBuff->node);

	return pSockBuff;
}

void GkSockBuffFree(struct SocketBuffer *pSockBuff)
{
	BUG_IF(!pSockBuff);

	free(pSockBuff->pSkbHead);
	free(pSockBuff);
}


struct sockaddr *getaddr(UINT32 nip)
{
	struct ListNode *iter;
	struct HostAddr *pHostAddr;
	ULONG ulPsr;
	struct sockaddr *addr = NULL;

	LockIrqPsr(ulPsr);

	LIST_FOR_EACH(iter, &g_listHost)
	{
		pHostAddr = MemToObj(iter, struct HostAddr, node);

		if (*(UINT32 *)pHostAddr->sockAddr.pbDesIP == nip)
		{			
			addr = &pHostAddr->sockAddr;
			break;
		}
	}

	UnlockIrqPsr(ulPsr);

	return addr;
}


int GuNetGetIP(void *pNetCtrl, UINT32 *pdwIP)
{
	struct SysConf *pSysConf;

	GuSysConfLoad(&pSysConf);

	if (NULL == pSysConf)
	{
		BUG();
		return -EIO;
	}

	*pdwIP = pSysConf->mNetConf.dwLocalIP;

	return 0;
}


int GuNetSetIP(void *pNetCtrl, UINT32 dwIP)
{
	struct SysConf *pSysConf;

	GuSysConfLoad(&pSysConf);

	if (NULL == pSysConf)
		return -EIO;

	pSysConf->mNetConf.dwLocalIP = dwIP;

	return 0;
}


int GuNetGetTftpSvr(UINT32 *pdwIP)
{
	struct SysConf *pSysConf;

	GuSysConfLoad(&pSysConf);

	if (NULL == pSysConf)
		return -EIO;

	*pdwIP = pSysConf->mNetConf.dwTftpSvrIP;

	return 0;
}


int GuNetSetTftpSvr(UINT32 dwIP)
{
	struct SysConf *pSysConf;

	GuSysConfLoad(&pSysConf);

	if (NULL == pSysConf)
		return -EIO;

	pSysConf->mNetConf.dwTftpSvrIP = dwIP;

	return 0;
}


int GuNetSetMask(void *pNetCtrl, UINT32 dwMask)
{
	struct SysConf *pSysConf;

	GuSysConfLoad(&pSysConf);

	if (NULL == pSysConf)
		return -EIO;

	pSysConf->mNetConf.dwNetMask = dwMask;

	return 0;
}

int GkNetDevRegister(struct NetDevice *pNetDev)
{
	g_pCurNetDev = pNetDev;

	return 0;
}

int GuNetGetMac(void *pNetCtrl, BYTE pMacAddr[6])
{
	memcpy(pMacAddr, g_pCurNetDev->vbMacAddr, MAC_ADDR_LEN);
	return 0;
}

int GuNetSetMac(void *pNetCtrl, const BYTE pMacAddr[6])
{
	if (NULL == g_pCurNetDev)
		return -ENODEV;

	g_pCurNetDev->SetMacAddr(g_pCurNetDev, pMacAddr);

	return 0;
}

struct NetDevice *GkNetDevNew()
{
	struct NetDevice *pNetDev;

	pNetDev = zalloc(sizeof(struct NetDevice));

	return pNetDev;
}
